<template>
  <div class="popover-box">
    <q-input v-if="filter && data.length"
             color="grey-5"
             placeholder="搜索"
             v-model="filterText"
             class="mg-tree-search-input q-mb-xs"
             clearable />
    <q-checkbox v-if="data.length"
                v-model="checkAll"
                color="secondary"
                keep-color
                label="全选"
                class="mg-tree-checkall" @input="toggleCheckAll"></q-checkbox>
    <q-tree :nodes="data"
            ref="mg-tree"
            color="light"
            control-color="secondary"
            text-color="dark"
            :expanded.sync="expandedList"
            :ticked.sync="ticked"
            :tick-strategy="strategy"
            :filter="filterText"
            no-nodes-label="暂无数据"
            no-results-label="未找到匹配数据"
            :node-key="nodeKey"
            class="mg-tree">
      <div :slot="`default-header`" slot-scope="prop" class="row items-center">
        <div>{{ prop.node[nodeLabel] }}</div>
      </div>
    </q-tree>
  </div>
</template>

<script>
import { QTree, QInput } from 'quasar'
import { updateNode, toggleTree, getNodes } from './util'
export default {
  name: 'MgTree',
  components: {
    QTree,
    QInput
  },
  props: {
    deep: { // 勾选策略是否同时勾选当前节点的所有子节点
      type: Boolean,
      default: false
    },
    data: { // 支持多棵树
      type: Array,
      required: true
    },
    filter: { // 是否支持过滤
      type: Boolean,
      default: true
    },
    nodeKey: { // 节点唯一标识名
      type: String,
      default: 'code'
    },
    nodeLabel: { // 数据中的显示字段名
      type: String,
      default: 'label'
    },
    initTicked: { // 初始勾选项 [code]
      type: Array,
      default: () => []
    },
    tickChange: { // 将勾选项通知父组件 参数为this.ticked
      type: Function,
      required: false
    }
  },
  data () {
    return {
      filterText: '', // 过滤关键字
      ticked: [], // 勾选的节点
      firstExpandList: [], // 展开第一层
      expandedList: [], // 默认展开的节点
      isInit: false, // 本次ticked操作是否来自init
      checkAll: false // 全选
    }
  },
  computed: {
    strategy () { // 勾选策略
      return this.deep ? 'leaf' : 'strict'
    },
    notFloorTree () { // 树结构是否大于一层
      // 至少一个元素的children属性值有效,则表示大于一层
      return this.data.some(item => Array.isArray(item.children) && item.children.length)
    },
    nodes () {
      let all = getNodes(this.data, [], this.deep)
      return all.map(_ => _[this.nodeKey])
    }
  },
  created () {
    this.init()
  },
  beforeDestroy () {
    // 将选中项通知父组件
    typeof this.tickChange === 'function' && this.tickChange(this.ticked)
  },
  watch: {
    ticked (list, preList) {
      this.checkAll = this.nodes.length === list.length

      if (this.isInit) { // 来自init的修改,可忽略
        this.isInit = false
        return
      }
      // 每次勾选或去勾选时,修改checked属性值(去重再比较)
      const listSet = new Set(list)
      const preListSet = new Set(preList)
      const len = listSet.size // 当前勾选个数
      const preLen = preListSet.size // 上一次勾选个数
      const isAdd = len > preLen // 本次操作为 勾选操作(true) 或 去勾选操作(false)

      let nodeList = isAdd ? list.slice(preLen) : preList.filter(item => !list.includes(item))

      if (this.notFloorTree) { // 树结构有多层,则遍历修改父节点属性
        this.data.forEach(node => {
          updateNode(nodeList, isAdd, node, this.nodeKey)
        })
      } else { // 树结构只有一层, 直接找到并修改节点属性即可
        this.data.forEach(node => (nodeList.includes(node[this.nodeKey]) && (node['checked'] = isAdd)))
      }
    },
    filterText (value) { // 输入搜索内容,则展开所有节点; 否则,只展开第一层
      if (value) {
        this.$children[1].expandAll()
      } else {
        this.expandedList = JSON.parse(JSON.stringify(this.firstExpandList))
      }
    }
  },
  methods: {
    init () {
      // 计算展开节点,默认展开第一层
      const list = []
      this.data.forEach(item => {
        list.push(item[this.nodeKey])
      })
      this.expandedList = list
      this.firstExpandList = JSON.parse(JSON.stringify(list))

      // 计算勾选节点
      this.isInit = true
      this.ticked = this.initTicked || []
    },
    toggleCheckAll (value) {
      toggleTree(this.data, this.checkAll, this.nodeKey, 'checked')
      this.ticked = value ? this.nodes : []
    }
  }
}
</script>

<style lang="stylus" scoped>
$text-color = #333
.popover-box
  padding 10px 15px
  .mg-tree-checkall
    margin 4px 0
    color $text-color
</style>
